#include "ClassLibrary.h"

#include <Core/ClassMeta.h>
#include <Core/Hash.h>

namespace MOE
{
	namespace System
	{

		// Constructs an empty class library.
		ClassLibrary::ClassLibrary()
			: mClassMetas()
		{
		}

		// Constructs a class library by importing all existing class metas from another library.
		ClassLibrary::ClassLibrary(const ClassLibrary& other)
			: mClassMetas()
		{
			ImportFrom(other);
		}

		// Destructs the class library. Non-imported class meta will be destroyed.
		ClassLibrary::~ClassLibrary()
		{
			for (tMetaMap::value_type& metaPair : mClassMetas)
			{
				if (!metaPair.second.mIsImported)
				{
					delete metaPair.second.mClassMeta;
				}
			}
			mClassMetas.clear();
		}

		// Add a new class meta.
		// If the new class meta has duplicated name as any existing class meta, this call will be ignored.
		void ClassLibrary::AddClassMeta(const Core::ClassMeta* classMeta)
		{
			if (!classMeta)
			{
				return;
			}

			Core::tHashValue hash = Core::StringToHash(classMeta->GetClassName());
			if (mClassMetas.find(hash) != mClassMetas.end())
			{
				return;
			}
			ClassMetaInfo entry = { classMeta, false };
			mClassMetas.insert(std::make_pair(hash, entry));
		}

		// Remove a class meta.
		// If the class meta is not imported from another library, it'll be destroyed.
		void ClassLibrary::RemoveClassMeta(const Core::char8* className)
		{
			Core::tHashValue hash = Core::StringToHash(className);
			tMetaMap::iterator iter = mClassMetas.find(hash);
			if (iter != mClassMetas.end() && !iter->second.mIsImported)
			{
				delete iter->second.mClassMeta;
				mClassMetas.erase(iter);
			}
		}

		// Get a class meta with this given name.
		const Core::ClassMeta& ClassLibrary::GetClassMeta(const Core::char8* className) const
		{
			Core::tHashValue hash = Core::StringToHash(className);
			tMetaMap::const_iterator iter = mClassMetas.find(hash);
			if (iter == mClassMetas.end())
			{
				return *iter->second.mClassMeta;
			}
			return Core::ClassMeta::kNil;
		}

		// Get the number of class metas.
		Core::tSize ClassLibrary::GetClassMetaCount() const
		{
			return mClassMetas.size();
		}

		// Iterate over all class meta.
		void ClassLibrary::ForEachClassMeta(const std::function<void(const Core::ClassMeta&)>& func) const
		{
			for (const tMetaMap::value_type& metaPair : mClassMetas)
			{
				func(*metaPair.second.mClassMeta);
			}
		}

		// Import classes from another class library.
		// Only used when constructed... for now.
		void ClassLibrary::ImportFrom(const ClassLibrary& other)
		{
			other.ForEachClassMeta([this](const MOE::Core::ClassMeta& meta){
				Core::tHashValue hash = Core::StringToHash(meta.GetClassName());
				if (mClassMetas.find(hash) != mClassMetas.end())
				{
					return;
				}
				ClassMetaInfo entry = { &meta, true };
				mClassMetas.insert(std::make_pair(hash, entry));
			});
		}

	}
}
